package main

import (
	"context"
	"errors"
	"fmt"
	"strconv"
	"sync"
	"time"

	"github.com/brianvoe/gofakeit/v6"
	"github.com/go-redis/redis/v8"
)

var client *redis.Client

// 只需要一个解锁的 lua 脚本，意思是自己加的锁自己才可以删除
const unlockScript = `
 if redis.call("get",KEYS[1]) == ARGV[1] then
	 return redis.call("del",KEYS[1])
 else
	 return 0
 end`

func lottery(ctx context.Context, name string) error {
	// 加锁
	myRandomValue := gofakeit.UUID()
	resourceName := "resource_name"
	ok, err := client.SetNX(ctx, resourceName, myRandomValue, time.Second*30).Result()
	if err != nil {
		return err
	}
	if !ok {
		return errors.New("系统繁忙，请重试")
	}

	// 解锁
	defer func() {
		redis.NewScript(unlockScript).Run(ctx, client, []string{resourceName}, myRandomValue)
	}()

	fmt.Println("get lock", name)
	// 业务处理
	time.Sleep(time.Second)

	return nil
}

func main() {
	client = redis.NewClient(&redis.Options{
		Addr: "127.0.0.1:6379",
	})

	if res, err := client.Ping(context.Background()).Result(); err != nil {
		panic(err)
	} else {
		fmt.Println(res)
	}

	var wg sync.WaitGroup
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func(name string) {
			defer wg.Done()

			ctx, _ := context.WithTimeout(context.Background(), time.Second*3)
			err := lottery(ctx, name)
			_ = err
		}(strconv.Itoa(i))
	}

	wg.Wait()
}
